(function() {
    
function HeightMap(sideLength, numSegments) {
    this.verts = [];
    this.numVertices = numSegments + 1;
    this.numSegments = numSegments;
    this.sideLength = sideLength;
    this.segLength = sideLength / numSegments;

    var x, y, z, C = 25;
    var mountain = function(z,x,z0,x0,h) {
        return 5*h*Math.pow(2, -((x-x0)/50)*((x-x0)/50) 
                                 -((z-z0)/50)*((z-z0)/50));
    }

    for (var i = 0; i < this.numVertices; i++) {
        for (var j = 0; j < this.numVertices; j++) {
	    
            z = (i - (this.numVertices-1)/2) * this.segLength;
            x = (j - (this.numVertices-1)/2) * this.segLength;

            y = 50/(118 - z) +
	           	50/(x + 118) +
		        14*Math.atan((z-150)/50) + 
                14*Math.atan((1.5*x - 2*z + 150)/50) +
                Math.min(mountain(z,x,0,200,20), 40) +
                Math.min(mountain(z,x,-70,300,10), 140) +
                Math.min(mountain(z,x,-30,420,30), 50);
            this.verts.push(y);
        }    
    }
};

HeightMap.prototype.getSlice = function(z, x, LOD) {

    var ix = Math.floor((x + this.sideLength/2) / this.segLength);
    var iz = Math.floor((z + this.sideLength/2) / this.segLength);

    var segmentsPerSlice = 32;
    var slicesPerSide = this.numSegments / segmentsPerSlice;
    var sliceSideLength = this.sideLength / slicesPerSide;
    var sliceix = Math.floor( ix / segmentsPerSlice );
    var sliceiz = Math.floor( iz / segmentsPerSlice );
    var sliceX = sliceix * segmentsPerSlice;
    var sliceZ = sliceiz * segmentsPerSlice;

    var stepLength = Math.round(Math.pow(2, LOD-1));
    var slice = {ixBegin:  sliceX,
            ixEnd:      sliceX + segmentsPerSlice + stepLength,
            ixStep:     stepLength,
            izBegin:    sliceZ,
            izEnd:      sliceZ + segmentsPerSlice + stepLength,
            izStep:     stepLength,
            segments:   segmentsPerSlice / stepLength,
            sideLength: this.sideLength / slicesPerSide,
            centerX:    this.sideLength * sliceix / slicesPerSide + sliceSideLength/2 - this.sideLength/2,
            centerZ:    this.sideLength * sliceiz / slicesPerSide + sliceSideLength/2 - this.sideLength/2};
    
    return slice;
};

HeightMap.prototype.get = function(iz, ix) {

    return this.verts[iz * this.numVertices + ix];
};

HeightMap.prototype.getHeight = function(z, x) {
   
    // Bilinear interpolation. I think...    
    var ix = Math.floor((x + this.sideLength/2) / this.segLength);
    var iz = Math.floor((z + this.sideLength/2) / this.segLength);

    var d = this.numVertices;
    var v = this.verts[iz * d + ix];
    var vx = this.verts[iz * d + ix + 1];
    var vz = this.verts[(iz + 1) * d + ix];
    var vxz = this.verts[(iz + 1) * d + ix + 1];
    var rz = z/this.segLength - Math.floor(z/this.segLength);
    var rx = x/this.segLength - Math.floor(x/this.segLength);
    
    var lpx1 = (1 - rx) * v + rx * vx;
    var lpx2 = (1 - rx) * vz + rx * vxz;
    
    return (1 - rz) * lpx1 + rz * lpx2;
};   

HeightMap.prototype.getNormal = function(z, x) {
   
    var h = this.getHeight(z, x);
    var hz = this.getHeight(z+1, x);
    var hx = this.getHeight(z, x+1);
    
    var vz = Vec(0, hz - h, 1);
    var vx = Vec(1, hx - h, 0);
    return vz.cross(vx).normalizeMe();
};   

if (typeof module !== 'undefined' &&
    typeof module.exports !== 'undefined') {
    module.exports = HeightMap;
} else {
    window.HeightMap = HeightMap;
}
})();
